import { get } from "lodash";
import qs from "qs";
import axios from "axios";

const successMessage = (msg) => {
    const instance = window.g_instance;
    instance.$message.success(msg)
}
const errorMessage = (msg) => {
    const instance = window.g_instance;
    instance.$message.error(msg)
}

import {
    getTokenInfo,
    setSessionCookie,
} from "@utils/session";


const pushLogin = (reason) => {
    console.error("跳到登录页！");
    requestManager.cancelAllRequests();
    window.g_instance.$store.dispatch('account/logout', { reason })
};

const isDev = () => isDev;

const instance = axios.create({
    // baseURL: `/${baseName}`,
    timeout: 20 * 1000,
});

instance.interceptors.request.use(function (config) {
    let serviceSet = config.serviceSet;

    if (serviceSet.requestFormatHandle) {
        config = { ...config, ...serviceSet.requestFormatHandle(config) };
    }

    if (serviceSet.formRequestSign) {
        config.headers["content-type"] = "application/x-www-form-urlencoded; charset=UTF-8";
        config.data = qs.stringify(config.data);
    }
    if (serviceSet.formDataObjectJsonSign) {
        const formData = new FormData();
        for (const [key, value] of Object.entries(config.data)) {
            let tempValue = value;
            if (typeof value === "object") {
                tempValue = JSON.stringify(value);
            }
            formData.append(key, tempValue);
        }
        config.data = formData;
    }
    if (serviceSet.formDataMultipartSign) {
        config.headers["content-type"] = "multipart/form-data";
    }
    if (serviceSet.contentType) {
        config.headers["content-type"] = serviceSet.contentType;
    }
    if (serviceSet.responseType) {
        config.responseType = serviceSet.responseType;
    }

    let tokenInfo = getTokenInfo();
    if (!serviceSet.notAuth) {
        if (!tokenInfo) {
            console.error(serviceSet.url, "需认证接口，当前无认证");
            pushLogin("没有tokenInfo，先登录");
            return Promise.reject(new Error("no auth"));
        }
        config.headers.Authorization = `${tokenInfo.token_type} ${tokenInfo.access_token}`;
        config.headers.access_token = `${tokenInfo.access_token}`;
    }

    return config;
});

instance.interceptors.response.use(
    function (response) {
        let config = response.config;
        let serviceSet = get(config, "serviceSet", {});
        let noMessageTip = config.noMessageTip || serviceSet.noMessageTip; // 成功的消息提示，按理说查询类接口不应提示查询成功类消息
        let noErrorMessageTip =
            config.noErrorMessageTip || serviceSet.noErrorMessageTip; // 失败提醒标识
        try {

            if (serviceSet.returnDataOnly) {
                return response.data;
            } else {
                let responseData = response.data || {};
                let resultMap = response;
                if (serviceSet.responseFormHandle) {
                    resultMap = serviceSet.responseFormHandle(responseData, response); // {success: Boolean, message: String, data: Object}

                    if (!resultMap.success) {
                        if (!noErrorMessageTip) {
                            errorMessage(resultMap.message || "服务错误");
                        }
                        return false;
                    }
                    if (resultMap.message && !noMessageTip) {
                        successMessage(resultMap.message);
                    }
                }



                let outData = resultMap.data !== undefined ? resultMap.data : true;

                return outData;
            }
        } catch (error) {
            return Promise.reject(error);
        }
    },
    function (error) {
        let response = error.response;

        if (!response) {
            // errorMessage("未查询到信息");
            return Promise.reject(error);
        }

        try {
            if (response.status === 401) {
                setSessionCookie();
                pushLogin('token失效登出');
                return Promise.reject(new Error("no auth"));
            }
            errorMessage("未查询到信息");

            return Promise.reject(error);
        } catch (error) {
            return Promise.reject(error);
        }
    }
);
let loadingTimes = 0;
let loadingInstance = null;


const getUrlBySet = (serviceSet) => {
    let prefix = ""; // 接口最终前缀
    let configPrefix = serviceSet.prefix; // 配置的接口前缀

    // 如果不是全路径就拼接url
    if (!serviceSet.isFullUrl && configPrefix) {
        if (typeof configPrefix === "function") {
            // 如果配置了，前缀从接口取
            prefix = configPrefix();
        } else {
            prefix = configPrefix || "";
        }
    }
    // 如果是开发环境，判断是否需要加入mock前缀
    if (isDev()) {
        if (serviceSet.mock) {
            prefix = "/mock" + prefix;
        }
    }
    return (
        prefix +
        (typeof serviceSet.url === "function"
            ? serviceSet.url(serviceSet)
            : serviceSet.url)
    );
};

class RequestManager {
    constructor() {
        this.cancelTokens = new Map(); // 缓存每个 CancelToken
    }

    // 添加请求
    addRequestCancelToken(configs) {
        const source = axios.CancelToken.source();

        configs = {
            ...(configs || {}),
            cancelToken: source.token,
        }
        this.cancelTokens.set(configs.requestId, source);
        return configs;
    }
    // 取消单个请求
    cancelByRequestId(requestId) {
        let source = this.cancelTokens.get(requestId)
        source && source.cancel(`权限过期 - Request ID: `, requestId)
        this.cancelTokens.delete(requestId)
    }
    // 取消所有请求
    cancelAllRequests(reason = 'Token expired') {
        this.cancelTokens.forEach((source, requestId) => {
            source.cancel(`${reason} - Request ID: `, requestId);
        });
        this.cancelTokens.clear();
    }
}
const requestManager = new RequestManager();
/**
 * @param {string} name 接口名
 * @param {object} serviceSet 接口配置 自定义一些接口配置，axios配置也一样传
 * @param {string} serviceSet.url 接口url，会拼接在prefix后 【必填】
 * @param {string | function } serviceSet.prefix 接口前缀，如果有会拼在url前
 * @param {boolean} serviceSet.noAddApi 接口前缀默认会追加/api，便于统一代理。true不添加
 * @param {boolean} serviceSet.notAuth 是否需要权限校验，如果为true，会校验token，校验失败会跳转登录页
 * @param {boolean} serviceSet.mock 是否添加mock前缀，如果为true，接口url前缀会追加/mock
 * @param {boolean} serviceSet.loading 是否显示加载中
 * @param {boolean} serviceSet.noMessageTip 是否取消成功消息提示
 * @param {boolean} serviceSet.noErrorMessageTip 是否取消失败消息提示
 * @param {boolean} serviceSet.formRequestSign 是否使用 application/x-www-form-urlencoded; charset=UTF-8 参数格式，会处理请求参数格式
 * @param {boolean} serviceSet.formDataObjectJsonSign 使用formData 格式提交  new FormData()
 * @param {boolean} serviceSet.formDataMultipartSign 会追加 multipart/form-data content-type 请求头
 * @param {string} serviceSet.contentType 会添加 content-type 值
 * @param {string} serviceSet.responseType 会在axios config中添加responseType字段
 *
 * @param {boolean} config.returnDataOnly 是否直接返回接口响应。默认接口响应会经过[responseDataFormHandle]处理来确定接口成功失败状态，如果想拿到原始响应，该值传true
 * @param {function} config.responseDataFormHandle 处理接口响应状态。返回值格式: {message:string, success: boolean, data: any} 最终data会被传递到业务层
 * @param {function} config.requestFormatHandle 请求参数处理函数
 */
export default (serviceSet) => {
    if (!serviceSet) {
        return Promise.reject(new Error("无此服务："));
    }
    try {
        let loading = serviceSet.loading;
        let loadingText = typeof loading === "string" ? loading : "";

        let url = getUrlBySet(serviceSet);

        let serviceConfig = {
            ...serviceSet,
            url,
            method: serviceSet.method || "post",
            serviceSet,
            requestId: Symbol(url), // 唯一标识
        };
        serviceConfig = requestManager.addRequestCancelToken(serviceConfig);
        if (loading) {
            loadingTimes++;
            loadingInstance = window.window.g_instance.$loading({
                fullscreen: true,
                background: "rgba(255, 255, 255, 0.1)",
                text: loadingText,
            });
        }
        return instance(serviceConfig).finally((e) => {
            loadingTimes--;
            if (loadingTimes <= 0) {
                loadingTimes = 0;
                loadingInstance && loadingInstance.close();
            }
            requestManager.cancelByRequestId(serviceConfig.requestId);
        });
    } catch (error) {
        return Promise.reject(error);
    }
};
